package com.chatbot.service;

import com.chatbot.dto.ChatAttachmentDTO;
import com.chatbot.dto.ChatMessageDTO;
import com.chatbot.dto.ConversationDTO;
import com.chatbot.entity.ChatAttachment;
import com.chatbot.entity.ChatMessage;
import com.chatbot.entity.ChatMessage.SenderType;
import com.chatbot.entity.Conversation;
import com.chatbot.entity.User;
import com.chatbot.repository.ChatAttachmentRepository;
import com.chatbot.repository.ChatMessageRepository;
import com.chatbot.repository.ConversationRepository;
import com.chatbot.repository.UserRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.*;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.stream.Collectors;

@Service
public class ChatService {

    private final ConversationRepository conversationRepository;
    private final ChatMessageRepository chatMessageRepository;
    private final ChatAttachmentRepository chatAttachmentRepository;
    private final UserRepository userRepository;
    private final RestTemplate restTemplate;
    private final String deepseekApiKey;
    private final String deepseekApiUrl;
    private final ObjectMapper objectMapper;
    
    @Value("${file.upload.dir}")
    private String fileUploadDir;

    public ChatService(ConversationRepository conversationRepository,
                      ChatMessageRepository chatMessageRepository,
                      ChatAttachmentRepository chatAttachmentRepository,
                      UserRepository userRepository,
                      @Value("${deepseek.apiKey}") String deepseekApiKey,
                      @Value("${deepseek.apiUrl}") String deepseekApiUrl) {
        this.conversationRepository = conversationRepository;
        this.chatMessageRepository = chatMessageRepository;
        this.chatAttachmentRepository = chatAttachmentRepository;
        this.userRepository = userRepository;
        this.restTemplate = new RestTemplate();
        this.deepseekApiKey = deepseekApiKey;
        this.deepseekApiUrl = deepseekApiUrl;
        this.objectMapper = new ObjectMapper();
    }

    @Transactional
    public ConversationDTO createConversation(String username, String title) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        Conversation conversation = new Conversation();
        conversation.setUser(user);
        conversation.setTitle(title);
        
        conversation = conversationRepository.save(conversation);
        return convertToDTO(conversation);
    }

    @Transactional
    public ChatMessageDTO sendMessage(String username, Long conversationId, String message) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        Conversation conversation = conversationRepository.findById(conversationId)
                .orElseThrow(() -> new RuntimeException("Conversation not found"));

        if (!conversation.getUser().getId().equals(user.getId())) {
            throw new RuntimeException("Unauthorized access to conversation");
        }

        // Save user message
        ChatMessage userMessage = new ChatMessage();
        userMessage.setConversation(conversation);
        userMessage.setContent(message);
        userMessage.setSenderType(SenderType.user);
        chatMessageRepository.save(userMessage);

        // Get AI response
        String aiResponse = getAiResponse(message);

        // Save AI response
        ChatMessage aiMessage = new ChatMessage();
        aiMessage.setConversation(conversation);
        aiMessage.setContent(aiResponse);
        aiMessage.setSenderType(SenderType.assistant);
        aiMessage = chatMessageRepository.save(aiMessage);

        return convertToDTO(aiMessage);
    }

    @Transactional
    public ChatMessageDTO sendMessageWithAttachment(String username, Long conversationId, String message, List<MultipartFile> files) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        Conversation conversation = conversationRepository.findById(conversationId)
                .orElseThrow(() -> new RuntimeException("Conversation not found"));

        if (!conversation.getUser().getId().equals(user.getId())) {
            throw new RuntimeException("Unauthorized access to conversation");
        }

        // 创建用户消息
        ChatMessage userMessage = new ChatMessage();
        userMessage.setConversation(conversation);
        userMessage.setContent(message != null ? message : "");
        userMessage.setSenderType(SenderType.user);
        userMessage = chatMessageRepository.save(userMessage);

        // 保存附件
        List<ChatAttachment> attachments = new ArrayList<>();
        StringBuilder fileContents = new StringBuilder();
        
        for (MultipartFile file : files) {
            try {
                // 创建上传目录
                Path uploadDir = Paths.get(fileUploadDir);
                if (!Files.exists(uploadDir)) {
                    Files.createDirectories(uploadDir);
                }

                // 生成唯一的文件名
                String originalFilename = file.getOriginalFilename();
                String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
                String uniqueFilename = System.currentTimeMillis() + "_" + userMessage.getId() + fileExtension;
                Path filePath = uploadDir.resolve(uniqueFilename);

                // 保存文件
                Files.copy(file.getInputStream(), filePath);

                // 创建附件记录
                ChatAttachment attachment = new ChatAttachment();
                attachment.setMessage(userMessage);
                attachment.setFileName(originalFilename);
                attachment.setFilePath(uniqueFilename);
                attachment.setFileType(file.getContentType());
                attachment.setFileSize(file.getSize());
                attachments.add(chatAttachmentRepository.save(attachment));

                // 如果是文本文件，读取内容
                if (file.getContentType() != null && 
                    (file.getContentType().startsWith("text/") || 
                     file.getContentType().equals("application/pdf") ||
                     file.getContentType().equals("application/msword") ||
                     file.getContentType().equals("application/vnd.openxmlformats-officedocument.wordprocessingml.document"))) {
                    
                    String content = new String(file.getBytes(), "UTF-8");
                    fileContents.append("文件 ").append(originalFilename).append(" 的内容：\n")
                              .append(content).append("\n\n");
                } else {
                    fileContents.append("收到文件：").append(originalFilename)
                              .append("（").append(formatFileSize(file.getSize())).append("）\n");
                }
            } catch (IOException e) {
                throw new RuntimeException("Failed to store file", e);
            }
        }

        // 构建完整的消息内容
        String completeMessage = "";
        if (message != null && !message.trim().isEmpty()) {
            completeMessage += message + "\n\n";
        }
        completeMessage += fileContents.toString();

        // 获取AI回复
        String aiResponse = getAiResponse(completeMessage);
        ChatMessage aiMessage = new ChatMessage();
        aiMessage.setConversation(conversation);
        aiMessage.setContent(aiResponse);
        aiMessage.setSenderType(SenderType.assistant);
        aiMessage = chatMessageRepository.save(aiMessage);

        // 返回用户消息（包含附件信息）
        return convertToDTO(userMessage, attachments);
    }

    private String getAiResponse(String message) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(deepseekApiKey);

        Map<String, Object> messageObj = new HashMap<>();
        messageObj.put("role", "user");
        messageObj.put("content", message);

        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("model", "deepseek-chat");
        requestBody.put("messages", List.of(messageObj));
        requestBody.put("temperature", 0.7);

        HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

        try {
            ResponseEntity<Map> response = restTemplate.exchange(
                deepseekApiUrl,
                HttpMethod.POST,
                entity,
                Map.class
            );

            List<Map<String, Object>> choices = (List<Map<String, Object>>) response.getBody().get("choices");
            Map<String, Object> firstChoice = choices.get(0);
            Map<String, Object> message_obj = (Map<String, Object>) firstChoice.get("message");
            return (String) message_obj.get("content");
        } catch (Exception e) {
            throw new RuntimeException("Failed to get response from Deepseek API: " + e.getMessage());
        }
    }

    public List<ConversationDTO> getUserConversations(String username) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        return conversationRepository.findByUserOrderByCreatedAtDesc(user)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    public ConversationDTO getConversation(String username, Long conversationId) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        Conversation conversation = conversationRepository.findById(conversationId)
                .orElseThrow(() -> new RuntimeException("Conversation not found"));

        if (!conversation.getUser().getId().equals(user.getId())) {
            throw new RuntimeException("Unauthorized access to conversation");
        }

        return convertToDTO(conversation);
    }

    private ConversationDTO convertToDTO(Conversation conversation) {
        ConversationDTO dto = new ConversationDTO();
        dto.setId(conversation.getId());
        dto.setTitle(conversation.getTitle());
        dto.setCreatedAt(conversation.getCreatedAt());
        
        List<ChatMessageDTO> messages = chatMessageRepository
                .findByConversationOrderByTimestampAsc(conversation)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        dto.setMessages(messages);
        return dto;
    }

    private ChatMessageDTO convertToDTO(ChatMessage message) {
        ChatMessageDTO dto = new ChatMessageDTO();
        dto.setId(message.getId());
        dto.setContent(message.getContent());
        dto.setRole(message.getSenderType().toString());
        dto.setCreatedAt(message.getTimestamp());
        return dto;
    }

    private ChatMessageDTO convertToDTO(ChatMessage message, List<ChatAttachment> attachments) {
        ChatMessageDTO dto = new ChatMessageDTO();
        dto.setId(message.getId());
        dto.setContent(message.getContent());
        dto.setRole(message.getSenderType().toString());
        dto.setCreatedAt(message.getTimestamp());
        
        if (attachments != null && !attachments.isEmpty()) {
            List<ChatAttachmentDTO> attachmentDTOs = attachments.stream()
                    .map(this::convertToAttachmentDTO)
                    .collect(Collectors.toList());
            dto.setAttachments(attachmentDTOs);
        }
        
        return dto;
    }

    private ChatAttachmentDTO convertToAttachmentDTO(ChatAttachment attachment) {
        ChatAttachmentDTO dto = new ChatAttachmentDTO();
        dto.setId(attachment.getId());
        dto.setFileName(attachment.getFileName());
        dto.setFileType(attachment.getFileType());
        dto.setFileSize(attachment.getFileSize());
        dto.setCreatedAt(attachment.getCreatedAt());
        dto.setDownloadUrl("/api/chat/attachments/" + attachment.getId());
        return dto;
    }

    @Transactional
    public SseEmitter streamMessage(String username, Long conversationId, String message) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        Conversation conversation = conversationRepository.findById(conversationId)
                .orElseThrow(() -> new RuntimeException("Conversation not found"));

        if (!conversation.getUser().getId().equals(user.getId())) {
            throw new RuntimeException("Unauthorized access to conversation");
        }

        // Save user message
        ChatMessage userMessage = new ChatMessage();
        userMessage.setConversation(conversation);
        userMessage.setContent(message);
        userMessage.setSenderType(SenderType.user);
        chatMessageRepository.save(userMessage);

        // Create SSE emitter
        SseEmitter emitter = new SseEmitter();
        
        // Start streaming in a separate thread
        Thread streamThread = new Thread(() -> {
            try {
                streamAiResponse(message, emitter, conversation);
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
        });
        streamThread.start();

        return emitter;
    }

    private void streamAiResponse(String message, SseEmitter emitter, Conversation conversation) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(deepseekApiKey);

        Map<String, Object> messageObj = new HashMap<>();
        messageObj.put("role", "user");
        messageObj.put("content", message);

        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("model", "deepseek-chat");
        requestBody.put("messages", List.of(messageObj));
        requestBody.put("temperature", 0.3);
        requestBody.put("stream", true);

        HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

        StringBuilder fullResponse = new StringBuilder();

        try {
            restTemplate.execute(
                deepseekApiUrl,
                HttpMethod.POST,
                request -> {
                    objectMapper.writeValue(request.getBody(), entity.getBody());
                    request.getHeaders().putAll(entity.getHeaders());
                },
                response -> {
                    try (var reader = response.getBody()) {
                        byte[] buffer = new byte[1024];
                        int bytesRead;
                        while ((bytesRead = reader.read(buffer)) != -1) {
                            String chunk = new String(buffer, 0, bytesRead);
                            String[] lines = chunk.split("\n");
                            
                            for (String line : lines) {
                                if (line.startsWith("data: ")) {
                                    String data = line.substring(6);
                                    if (!"[DONE]".equals(data)) {
                                        try {
                                            Map<String, Object> responseData = objectMapper.readValue(data, Map.class);
                                            List<Map<String, Object>> choices = (List<Map<String, Object>>) responseData.get("choices");
                                            if (choices != null && !choices.isEmpty()) {
                                                Map<String, Object> choice = choices.get(0);
                                                Map<String, Object> delta = (Map<String, Object>) choice.get("delta");
                                                if (delta != null && delta.containsKey("content")) {
                                                    String content = (String) delta.get("content");
                                                    if (content != null && !content.trim().isEmpty()) {
                                                        fullResponse.append(content);
                                                        emitter.send(content);
                                                    }
                                                }
                                            }
                                        } catch (Exception e) {
                                            // Skip malformed JSON
                                        }
                                    }
                                }
                            }
                        }
                    } catch (IOException e) {
                        throw new RuntimeException("Error reading stream", e);
                    }
                    return null;
                }
            );

            // Save the complete AI response
            ChatMessage aiMessage = new ChatMessage();
            aiMessage.setConversation(conversation);
            aiMessage.setContent(fullResponse.toString());
            aiMessage.setSenderType(SenderType.assistant);
            chatMessageRepository.save(aiMessage);

            emitter.complete();
        } catch (Exception e) {
            emitter.completeWithError(e);
        }
    }

    @Transactional
    public void deleteConversation(String username, Long conversationId) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        Conversation conversation = conversationRepository.findById(conversationId)
                .orElseThrow(() -> new RuntimeException("Conversation not found"));

        if (!conversation.getUser().getId().equals(user.getId())) {
            throw new RuntimeException("Unauthorized access to conversation");
        }

        // The messages will be automatically deleted due to CASCADE
        conversationRepository.delete(conversation);
    }

    public Resource downloadAttachment(String username, Long attachmentId) {
        ChatAttachment attachment = chatAttachmentRepository.findById(attachmentId)
                .orElseThrow(() -> new RuntimeException("Attachment not found"));

        // 验证用户权限
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        if (!attachment.getMessage().getConversation().getUser().getId().equals(user.getId())) {
            throw new RuntimeException("Unauthorized access to attachment");
        }

        try {
            Path file = Paths.get(fileUploadDir).resolve(attachment.getFilePath());
            Resource resource = new UrlResource(file.toUri());
            if (resource.exists() || resource.isReadable()) {
                return resource;
            } else {
                throw new RuntimeException("Could not read the file!");
            }
        } catch (MalformedURLException e) {
            throw new RuntimeException("Error: " + e.getMessage());
        }
    }

    private String formatFileSize(long size) {
        if (size < 1024) return size + " B";
        int z = (63 - Long.numberOfLeadingZeros(size)) / 10;
        return String.format("%.1f %sB", (double)size / (1L << (z*10)), " KMGTPE".charAt(z));
    }
} 